[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto
skywatched.app
6.6 kB
194 lines
1<script lang="ts">
2 import { type PageData } from './$types';
3 import { cn, nameToId } from '$lib/utils';
4 import { rateMovieModal, videoPlayer, watchedItems } from '$lib/state.svelte';
5
6 import Container from '$lib/Components/Container.svelte';
7 import ItemsList from '$lib/Components/ItemsList.svelte';
8 import ReviewList from '$lib/Components/ReviewList.svelte';
9 import Avatar from '$lib/Components/Avatar.svelte';
10 import { page } from '$app/stores';
11
12 let { data }: { data: PageData } = $props();
13</script>
14
15<svelte:head>
16 <title>{data.result.title ?? data.result.name ?? ''} | skywatched</title>
17
18 <meta
19 name="description"
20 content={`Rate and review "${data.result.title ?? data.result.name ?? ''}" on skywatched`}
21 />
22
23 <meta property="og:url" content={$page.url.href} />
24 <meta property="og:type" content="website" />
25 <meta property="og:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched.app" />
26 <meta
27 property="og:description"
28 content={`Rate and review "${data.result.title ?? data.result.name ?? ''}" on skywatched`}
29 />
30 <meta property="og:image" content="{$page.url.href}/og.png" />
31
32 <meta name="twitter:card" content="summary_large_image" />
33 <meta property="twitter:domain" content="skywatched.app" />
34 <meta property="twitter:url" content={$page.url.href} />
35 <meta name="twitter:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched.app" />
36 <meta
37 name="twitter:description"
38 content={`Rate and review "${data.result.title ?? data.result.name ?? ''}" on skywatched`}
39 />
40 <meta name="twitter:image" content="{$page.url.href}/og.png" />
41</svelte:head>
42
43{#snippet buttons()}
44 {#if data.user && !watchedItems.hasRated(data.result)}
45 <button
46 class={cn(
47 'inline-flex items-center gap-2 rounded-md bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-75 hover:bg-white/20 ',
48 watchedItems.hasRated(data.result)
49 ? 'bg-green-500/10 text-green-400 hover:bg-green-500/20'
50 : ''
51 )}
52 onclick={() => {
53 rateMovieModal.show({
54 movieId: data.result.movieId,
55 showId: data.result.showId,
56 kind: data.result.movieId ? 'movie' : 'tv',
57 name: data.result.original_title ?? data.result.original_name,
58 posterPath: data.result.poster_path,
59 currentRating: watchedItems.getRating(data.result)?.rating ?? 0,
60 currentReview: watchedItems.getRating(data.result)?.ratingText ?? ''
61 });
62 }}
63 >
64 rate {data.kind === 'movie' ? 'movie' : 'show'}
65 </button>
66 {/if}
67 {#if data.trailer}
68 <button
69 onclick={() => videoPlayer.show(data.trailer ?? '')}
70 type="button"
71 class="inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
72 >
73 <svg
74 xmlns="http://www.w3.org/2000/svg"
75 viewBox="0 0 24 24"
76 fill="currentColor"
77 class="-ml-0.5 size-5"
78 >
79 <path
80 d="M4.5 4.5a3 3 0 0 0-3 3v9a3 3 0 0 0 3 3h8.25a3 3 0 0 0 3-3v-9a3 3 0 0 0-3-3H4.5ZM19.94 18.75l-2.69-2.69V7.94l2.69-2.69c.944-.945 2.56-.276 2.56 1.06v11.38c0 1.336-1.616 2.005-2.56 1.06Z"
81 />
82 </svg>
83
84 Trailer
85 </button>
86 {/if}
87{/snippet}
88
89<img
90 src="https://image.tmdb.org/t/p/w780{data.result.backdrop_path}"
91 alt=""
92 class="fixed h-full w-full object-cover object-center opacity-20"
93/>
94<div class="fixed inset-0 h-full w-full bg-black/50"></div>
95
96<Container class="relative z-10 pb-8 pt-4">
97 <div class="flex gap-4 px-4 pt-8">
98 <img
99 src="https://image.tmdb.org/t/p/w500{data.result.poster_path}"
100 alt=""
101 class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
102 style:--name={`poster-${data.result.id}`}
103 />
104 <div class="flex flex-col gap-4">
105 <div
106 class="title max-w-xl text-2xl font-semibold text-white sm:text-4xl"
107 style:--name={`title-${data.result.id}`}
108 >
109 {data.result.title ?? data.result.name}
110 </div>
111
112 {#if data.settings?.streaming_region?.code && data.watchProviders[data.settings.streaming_region.code]?.flatrate}
113 <div class="mt-2 text-sm text-white sm:mt-4">
114 <div class="mb-2 flex flex-wrap gap-4 text-xs font-medium">
115 stream on
116 <span class="flex items-center gap-2 text-base-400"
117 >from <a
118 href="https://www.justwatch.com"
119 target="_blank"
120 class="text-base-300 hover:text-accent-300"
121 >
122 <img src="/justwatch_logo.svg" alt="justwatch" class="h-3" />
123 </a></span
124 >
125 </div>
126 <a
127 href={data.watchProviders[data.settings.streaming_region.code].link}
128 target="_blank"
129 class="flex flex-wrap gap-2"
130 >
131 {#each data.watchProviders[data.settings.streaming_region.code].flatrate as provider}
132 <img
133 src="https://image.tmdb.org/t/p/w500{provider.logo_path}"
134 alt={provider.provider_name}
135 class="size-8 rounded-md border border-base-800 md:size-12"
136 />
137 {/each}
138 </a>
139 </div>
140 {/if}
141 <div class="hidden gap-2 sm:flex">
142 {@render buttons()}
143 </div>
144 </div>
145 </div>
146
147 <div class="px-4 pt-4 text-sm text-white">
148 <div class="mb-4 flex gap-2 sm:hidden">
149 {@render buttons()}
150 </div>
151 <div class="mb-4 max-w-2xl text-sm text-white">
152 <div class="mb-2 text-lg font-semibold">overview</div>
153 {data.result.overview}
154 </div>
155 </div>
156
157 {#if data.ratings.length > 0}
158 <div class="mt-8 px-4 text-lg font-semibold">reviews</div>
159
160 <ReviewList reviews={data.ratings} showMovieDetails={false} class="" />
161 {/if}
162
163 {#if data.recommendations.length > 0}
164 <div class="px-4 pt-4 text-sm text-white">
165 <div class="mb-2 text-lg font-semibold">recommendations</div>
166
167 <ItemsList items={data.recommendations} showMark={!!data.user} />
168 </div>
169 {/if}
170
171 {#if data.cast.length > 0}
172 <div class="px-4 pb-8 pt-4 text-sm text-white">
173 <div class="mb-2 text-lg font-semibold">cast</div>
174
175 <div class={cn('flex gap-x-6 overflow-x-auto')}>
176 {#each data.cast as castMember}
177 <a
178 href={`/cast/${castMember.id}-${nameToId(castMember.name)}`}
179 class="flex flex-col items-center gap-1"
180 >
181 <Avatar
182 src={castMember.profile_path
183 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
184 : undefined}
185 size="size-32"
186 />
187 <div class="text-center text-xs font-medium">{castMember.name}</div>
188 <div class="text-center text-xs text-base-400">{castMember.character}</div>
189 </a>
190 {/each}
191 </div>
192 </div>
193 {/if}
194</Container>